<?php
header('Content-Type: application/json; charset=utf-8');
require $_SERVER['DOCUMENT_ROOT'] . '/vendor/autoload.php';
use Aws\S3\S3Client;
use Aws\Exception\AwsException;
function delete_urlsigndownload($bucket, $objectKey, $accessKeyId, $secretAccessKey)
{
    $region = 'rainyun';
    $s3Client = new S3Client([
        'version' => 'latest',
        'region' => $region,
        'endpoint' => 'https://cn-sy1.rains3.com',
        'credentials' => [
            'key' => $accessKeyId,
            'secret' => $secretAccessKey
        ],
        'http' => [
            'verify' => false,
        ],
        'use_path_style_endpoint' => true,
        'suppress_php_deprecation_warning' => true
    ]);

    try {
        $s3Client->deleteObject([
            'Bucket' => $bucket,
            'Key' => $objectKey
        ]);
    } catch (AwsException $e) {
        echo json_encode(['error' => 'Error: ' . $e->getMessage(), 'status' => 0]);
    }
}
function leftspace_check($comparetovalue)
{
    $dir = $_SERVER['DOCUMENT_ROOT'] . '/';
    $free_space = disk_free_space($dir);
    $nowleft = round($free_space / pow(1024, 3), 2);
    if ($nowleft > $comparetovalue) {
        return true;
    } else {
        return false;
    }
}
require($_SERVER['DOCUMENT_ROOT'] . '/setting.php');
function verifyToken($token)
{
    global $redis_pz;
    $redis = new Redis();
    $redis->connect($redis_pz['host'], $redis_pz['port']);
    $redis->auth($redis_pz['password']);
    $data = $redis->get($token);
    if ($data) {
        $data = json_decode($data, true);
        if ($data['timestamp'] > time()) {
            return true;
        }
    }
    return false;
}
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    echo json_encode(['success' => false, 'message' => 'error: requests are not allowed']);
    exit;
}
function generate_fileid($conn, $length = 7)
{
    $letters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
    $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
    $id = '';
    $id .= $letters[rand(0, strlen($letters) - 1)];
    $length -= 1;
    do {
        $id = '';
        for ($i = 0; $i < $length; $i++) {
            $id .= $characters[rand(0, strlen($characters) - 1)];
        }
        $result = $conn->query("SELECT 1 FROM files WHERE id = '$id'");
        if ($result !== false && $result->num_rows > 0) {
            $id = '';
        } else {
            break;
        }
    } while ($id === '');
    return $id;
}
function token_file_setone($headed, $uploadided, $fileidsss)
{
    global $redis_pz;
    $redis = new Redis();
    $redis->connect($redis_pz['host'], $redis_pz['port']);
    $redis->auth($redis_pz['password']);
    $token = $headed;
    $ttl = 3600 * 8;
    $timestamp = time() + $ttl;
    $data = [
        'authcode' => $token,
        'uploadid' => $uploadided,
        'onepie' => [],
        'timestamp' => $timestamp,
        'fileid' => $fileidsss
    ];
    $redis->setex($token, $ttl, json_encode($data));
    return true;
}
function duetofileid_getuploadid($headed)
{
    global $redis_pz;
    $redis = new Redis();
    $redis->connect($redis_pz['host'], $redis_pz['port']);
    $redis->auth($redis_pz['password']);
    $data = $redis->get($headed);
    if ($data) {
        $data = json_decode($data, true);
        if ($data['timestamp'] > time()) {
            return $data['uploadid'];
        }
    }
    return -1;
}
require($_SERVER['DOCUMENT_ROOT'] . '/cofd/functions.php');
$maxFileSize = 100 * 1024 * 1024;
$token = $_SERVER['HTTP_X_FILE_TOKEN_S'] ?? '';
$userid = $_SERVER['HTTP_X_FILE_TOKEN_E'] ?? '';
$password = $_SERVER['HTTP_X_FILE_PASSWORD'] ?? '';
$chunkindex = $_SERVER['HTTP_X_FILE_CHUNKINDEX'] ?? 0;
$totalchunks = $_SERVER['HTTP_X_FILE_TOTALCHUNKS'] ?? 0;
$uploadid_fz = urldecode($_SERVER['HTTP_X_FILE_UPLOADID']) ?? '';
$dicid_folder = $_SERVER['HTTP_X_FILE_DICID'] ?? '/';
$uploadDir = $_SERVER['DOCUMENT_ROOT'] . '/uploads/visitor';
if ($userid != 'visitor') {
    $uploadDir = $_SERVER['DOCUMENT_ROOT'] . '/uploads/' . $userid;
}
$fileName = basename(urldecode($_SERVER['HTTP_X_FILE_NAME']) ?? '');
$fileType = urldecode($_SERVER['HTTP_X_FILE_TYPE']) ?? '';
$contentRange = $_SERVER['HTTP_CONTENT_RANGE'] ?? '';
if ($token != '') {
    $token  = encrypt($token, 'D', 'yhsdylpo9342');
} else {
    echo json_encode(['success' => false, 'message' => 'invalid token']);
    exit;
}
if (!file_exists($uploadDir)) {
    mkdir($uploadDir, 0777, true);
}
$filePath = $uploadDir . '/' . $fileName;
if (!verifyToken($token)) {
    echo json_encode(['success' => false, 'message' => 'invalid token']);
    exit;
}
$contentLength = isset($_SERVER['HTTP_X_FILE_SIZEALL']) ? $_SERVER['HTTP_X_FILE_SIZEALL'] : 0;
if($userid == 'visitor'){
    if($contentLength > 1024 *1024 * $website_array['notlogined2']){  
        echo json_encode(['success' => false, 'message' => 'file size is too large']);
        exit;
    }
}
$sizeInGB = $contentLength / (1024 * 1024 * 1024);
if ($contentLength < pow(1024, 3)) {
    $precision = 6;
} else if ($contentLength < pow(1024, 4)) {
    $precision = 4;
} else {
    $precision = 2;
}
$contentLengthGB = number_format($sizeInGB, $precision, '.', '');
$contentLengthGB_f = $contentLengthGB;
if (!leftspace_check($contentLengthGB)) {
    if ($userid == 'visitor') {
        echo json_encode(['success' => false, 'message' => 'error: not enough space']);
        $stmt->close();
        $conn->close();
        exit;
    }
    try {
        $s3Client = new S3Client([
            'version' => 'latest',
            'region' => 'rainyun',
            'endpoint' => $rainyun_oss['endpoint'],
            'credentials' => [
                'key' => $rainyun_oss['access_key_id'],
                'secret' => $rainyun_oss['access_key_secret'],
            ],
            'http' => [
                'verify' => false,
            ],
            'use_path_style_endpoint' => true,
            'suppress_php_deprecation_warning' => true


        ]);
    } catch (Exception $e) {
        echo json_encode(['success' => false, 'message' => 'error: unable to connect to S3']);
        $stmt->close();
        $conn->close();
        exit;
    }
    if (preg_match('/bytes (\d+)-(\d+)\/(\d+)/', $contentRange, $matches)) {
        $startByte = (int)$matches[1];
        $endByte = (int)$matches[2];
        $totalSize = (int)$matches[3];
    } else {
        echo json_encode(['success' => false, 'message' => 'error: invalid Content-Range header']);
        $stmt->close();
        $conn->close();
        exit;
    }
    $chunkData = file_get_contents('php://input');
    if ($chunkData === false) {
        echo json_encode(['success' => false, 'message' => 'error: unable to read chunk data']);
        $stmt->close();
        $conn->close();
        exit;
    }
    $uploadId = null;
    require($_SERVER['DOCUMENT_ROOT'] . '/cofd/common.php');
    if ($chunkindex == 1) {
        $fileid = generate_fileid($conn);
        try {
            $initResult = $s3Client->createMultipartUpload([
                'Bucket' => $rainyun_oss['bucket'],
                'Key'    => $fileid . '_' . $fileName,
            ]);
            $uploadId = $initResult['UploadId'];
            token_file_setone($uploadid_fz, $uploadId, $fileid);
        } catch (AwsException $e) {
            echo json_encode(['success' => false, 'message' => 'error']);
            $stmt->close();
            $conn->close();
            exit;
        }
    } else {
        $uploadId = duetofileid_getuploadid($uploadid_fz);
        if ($uploadId == -1) {
            echo json_encode(['success' => false, 'message' => 'error: upload id not found']);
            $stmt->close();
            $conn->close();
            exit;
        }
    }
    $redis = new Redis();
    $redis->connect($redis_pz['host'], $redis_pz['port']);
    $redis->auth($redis_pz['password']);
    $data = $redis->get($uploadid_fz);
    if ($data) {
        $data = json_decode($data, true);
    }
    $fileid = $data['fileid'];
    $result = $s3Client->uploadPart([
        'Bucket'     => $rainyun_oss['bucket'],
        'Key'        => $fileid . '_' . $fileName,
        'UploadId'   => $uploadId,
        'PartNumber' => $chunkindex,
        'Body'       => $chunkData,
        'ContentLength' => strlen($chunkData),
        'ContentType' => $fileType,
    ]);
    if ($result['@metadata']['statusCode'] != 200) {
        echo json_encode(['success' => false, 'message' => 'error: unable to upload chunk data']);
        $stmt->close();
        $conn->close();
        exit;
    }
    $etag = $result['ETag'];
    $partNumber = $chunkindex;
    $data = $redis->get($uploadid_fz);
    if ($data) {
        $data = json_decode($data, true);
    } else {
        $data = [
            'authcode' => $uploadid_fz,
            'uploadid' => $uploadId,
            'onepie' => [],
            'timestamp' => time() + 3600 * 8
        ];
    }
    $data['onepie'][] = [
        'ETag' => $etag,
        'PartNumber' => $partNumber
    ];
    $redis->setex($uploadid_fz, $data['timestamp'] - time(), json_encode($data));
    if ($endByte + 1 === $totalSize) {
        $data = $redis->get($uploadid_fz);
        if (!$data) {
            echo json_encode(['success' => false, 'message' => 'error: upload data not found in Redis']);
            $stmt->close();
            $conn->close();
            exit;
        }
        $data = json_decode($data, true);
        $parts = $data['onepie'];
        try {
            $result = $s3Client->completeMultipartUpload([
                'Bucket' => $rainyun_oss['bucket'],
                'Key' => $fileid . '_' . $fileName,
                'UploadId' => $uploadId,
                'MultipartUpload' => [
                    'Parts' => $parts,
                ],
            ]);
            if ($result['@metadata']['statusCode'] != 200) {
                echo json_encode(['success' => false, 'message' => 'error: unable to complete multipart upload']);
                $stmt->close();
                $conn->close();
                exit;
            }
            $redis->unlink($uploadid_fz);
        } catch (Exception $e) {
            echo json_encode(['success' => false, 'message' => 'error: unable to complete multipart upload']);
            $stmt->close();
            $conn->close();
            exit;
        }
        if ($userid != 'visitor') {
            $nowforderandpluscontentLength = $contentLengthGB;
            $stmt = $conn->prepare("SELECT capacity,capacityed,vip FROM user WHERE id = ?");
            $stmt->bind_param("s", $userid);
            $stmt->execute();
            $result = $stmt->get_result();
            if ($result->num_rows > 0) {
                $row = $result->fetch_assoc();
                $capacity = $row['capacity'];
                $capacityed = $row['capacityed'];
                $vip = $row['vip'];
                $currentDateTime = new DateTime(); 
                $vipnowtime = new DateTime($vip);
                $nowforderandpluscontentLength = $capacityed + $contentLengthGB;
            } else {
                delete_urlsigndownload($rainyun_oss['bucket'],$fileid . '_' . $fileName,$rainyun_oss['access_key_id'],$rainyun_oss['access_key_secret']);
                echo json_encode(['success' => false, 'message' => 'error: invalid userid']);
                $stmt->close();
                $conn->close();
                exit;
            }
            if ($nowforderandpluscontentLength > $capacity) {
                if($vipnowtime<$currentDateTime){
                    delete_urlsigndownload($rainyun_oss['bucket'],$fileid . '_' . $fileName,$rainyun_oss['access_key_id'],$rainyun_oss['access_key_secret']);
                    echo json_encode(['success' => false, 'message' => 'error: not enough space']);
                    $stmt->close();
                    $conn->close();
                    exit;
                }
            }
        }
        $timenow = date('Y-m-d H:i:s');
        $rowtosqlname = "s3::>" . $fileid . '_' . $fileName;
        $numtodown = -1;
        $sql = "INSERT INTO files (id, names,pwd,froms,timei,asets,numd,isdic,sizes) VALUES (?, ?, ?, ?, ?, ?,?,?,?)";
        $stmt = $conn->prepare($sql);
        $stmt->bind_param("ssssssisd", $fileid, $fileName, $password, $userid, $timenow, $rowtosqlname, $numtodown,$dicid_folder,$contentLengthGB_f);
        if ($stmt->execute()) {
            $sql = "UPDATE user SET capacityed=capacityed+? WHERE id=?";
            $stmt = $conn->prepare($sql);
            $stmt->bind_param("ds", $contentLengthGB_f, $userid);
            if ($stmt->execute()) {
                echo json_encode(['success' => true, 'message' => 'file uploaded successfully']);
            } else {
                delete_urlsigndownload($rainyun_oss['bucket'],$fileid . '_' . $fileName,$rainyun_oss['access_key_id'],$rainyun_oss['access_key_secret']);
                echo json_encode(['success' => false, 'message' => 'error: failed to upload file']);
            }
        } else {
            delete_urlsigndownload($rainyun_oss['bucket'],$fileid . '_' . $fileName,$rainyun_oss['access_key_id'],$rainyun_oss['access_key_secret']);
            echo json_encode(['success' => false, 'message' => 'error: unable to complete multipart upload']);
        }
        $stmt->close();
        $conn->close();
    } else {
        echo json_encode(['success' => true, 'message' => 'chunk uploaded successfully']);
    }
} else {
    if (preg_match('/bytes (\d+)-(\d+)\/(\d+)/', $contentRange, $matches)) {
        $startByte = (int)$matches[1];
        $endByte = (int)$matches[2];
        $totalSize = (int)$matches[3];
    } else {
        echo json_encode(['success' => false, 'message' => 'error: invalid Content-Range header']);
        $stmt->close();
        $conn->close();
        exit;
    }
    if (!file_exists($filePath)) {
        $file = fopen($filePath, 'wb');
        fclose($file);
    }
    $file = fopen($filePath, 'r+b');
    if ($file === false) {
        echo json_encode(['success' => false, 'message' => 'error: unable to open file for writing']);
        $stmt->close();
        $conn->close();
        exit;
    }
    fseek($file, $startByte);
    $chunkData = file_get_contents('php://input');
    if ($chunkData === false) {
        echo json_encode(['success' => false, 'message' => 'error: unable to read chunk data']);
        $stmt->close();
        $conn->close();
        exit;
    }
    $bytesWritten = fwrite($file, $chunkData);
    if ($bytesWritten === false || $bytesWritten !== ($endByte - $startByte + 1)) {
        echo json_encode([
            'success' => false,
            'message' => "hope" . ($endByte - $startByte + 1) . "but" . $bytesWritten . " kb"
        ]);
        fclose($file);
        $stmt->close();
        $conn->close();
        exit;
    }
    fflush($file);
    fclose($file);
    if ($endByte + 1 === $totalSize) {
        if (filesize($filePath) !== $totalSize) {
            echo json_encode(['success' => false, 'message' => 'error: file size mismatch!']);
            $stmt->close();
            $conn->close();
            exit;
        }
        require($_SERVER['DOCUMENT_ROOT'] . '/cofd/common.php');
        if ($userid != 'visitor') {
            $nowforderandpluscontentLength = $contentLengthGB;
            $stmt = $conn->prepare("SELECT capacity,capacityed,vip FROM user WHERE id = ?");
            $stmt->bind_param("s", $userid);
            $stmt->execute();
            $result = $stmt->get_result();
            if ($result->num_rows > 0) {
                $row = $result->fetch_assoc();
                $capacity = $row['capacity'];
                $capacityed = $row['capacityed'];
                $vip = $row['vip'];
                $currentDateTime = new DateTime(); 
                $vipnowtime = new DateTime($vip);
                $nowforderandpluscontentLength = $capacityed + $contentLengthGB;
            } else {
                unlink($filePath);
                echo json_encode(['success' => false, 'message' => 'error: invalid userid']);
                $stmt->close();
                $conn->close();
                exit;
            }
            if ($nowforderandpluscontentLength > $capacity) {
                if($vipnowtime<$currentDateTime){
                    unlink($filePath);
                    echo json_encode(['success' => false, 'message' => 'error: not enough space']);
                    $stmt->close();
                    $conn->close();
                    exit;
                }
            }
        }
        $rowtosqlname = '';
        $numtodownload = -1;
        $fileid = generate_fileid($conn);
        if ($userid == 'visitor') {
            $rowtosqlname = 'visitor/' . $fileid . '_' . basename(urldecode($_SERVER['HTTP_X_FILE_NAME']) ?? '');
            $numtodownload = 2;
        } else {
            $rowtosqlname = $userid . '/' . $fileid . '_' . basename(urldecode($_SERVER['HTTP_X_FILE_NAME']) ?? '');
        }
        $timenow = date('Y-m-d H:i:s');
        $sql = "INSERT INTO files (id, names,pwd,froms,timei,asets,numd,isdic,sizes) VALUES (?, ?, ?, ?, ?, ?,?,?,?)";
        $stmt = $conn->prepare($sql);
        $dicis = 0;
        $stmt->bind_param("ssssssisd", $fileid, $fileName, $password, $userid, $timenow, $rowtosqlname, $numtodownload, $dicid_folder, $contentLengthGB_f);
        if ($stmt->execute()) {
            rename($filePath, $uploadDir . '/' . $fileid . '_' . $fileName);
            $sql = "UPDATE user SET capacityed=capacityed+? WHERE id=?";
            $stmt = $conn->prepare($sql);
            $stmt->bind_param("ds", $contentLengthGB_f, $userid);
            if ($stmt->execute()) {
                echo json_encode(['success' => true, 'message' => 'file uploaded successfully']);
            } else {
                unlink($filePath);
                echo json_encode(['success' => false, 'message' => 'error: failed to upload file']);
            }
        } else {
            unlink($filePath);
            echo json_encode(['success' => true, 'message' => 'chunk uploaded successfully']);
        }
        $stmt->close();
        $conn->close();
    } else {
        echo json_encode(['success' => true, 'message' => 'chunk uploaded successfully']);
    }
}
